package org.example.new_hbps_backend_reconstruction.controller.patient;

/**
 * @author 丁利军
 * @date 2025年10月14日 11:27
 * @description 心永企业 access_token 获取接口
 */

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.example.new_hbps_backend_reconstruction.common.results.R;
import org.example.new_hbps_backend_reconstruction.utils.HTTPUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

/**
 * 企业 access_token 获取接口
 * 文档地址: https://open.heart-forever.com/api/ext/getToken
 */
// --- OpenAPI / Swagger 相关 ---
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;

@Slf4j
@RestController
@RequestMapping("/api/token")
@Tag(
        name = "企业 Token 管理",
        description = "提供从心永开放平台获取企业 access_token 的接口,注意这里不是user_access_token"
)
// 若此接口无需鉴权，可删除下一行；若需要，请根据网关/安全方案调整 name
//@SecurityRequirement(name = "Authorization")
public class AccessTokenController {

    @Autowired
    private HTTPUtils httpUtils;

    // 你在心永后台申请的 appid 和 secret（生产建议改为配置注入）
    private static final String APP_ID = "bp_ezjsiejrziojwieo";
    private static final String APP_SECRET = "sec_ijd8gh237ysoikq9djjj";
    private static final String TOKEN_URL = "https://open.heart-forever.com/api/ext/getToken";

    /**
     * 获取企业 access_token
     */
    @Operation(
            summary = "获取企业 access_token",
            description = """
                    根据心永开放平台签名规则，生成 `nonce`、`timestamp` 和 `signature`，\
                    调用平台接口换取企业 `access_token`。\
                    默认签名规则：`signature = md5(appid + nonce + timestamp + appSecret)`。\
                    若平台文档有更新，请以平台文档为准并同步调整签名算法。
                    """,
            // 如果此接口对外不需要携带鉴权，可在这里覆盖声明为不需要
            security = {}
    )
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "获取成功",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = R.class),
                            examples = {
                                    @ExampleObject(
                                            name = "成功示例",
                                            value = """
                                                    {
                                                      "code": 0,
                                                      "msg": "获取成功",
                                                      "data": "eyJhbGciOi...token_example..."
                                                    }
                                                    """
                                    )
                            }
                    )),
            @ApiResponse(responseCode = "400", description = "请求参数错误",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = R.class),
                            examples = @ExampleObject(
                                    value = """
                                            { "code": 400, "msg": "获取失败: 请求参数错误", "data": null }
                                            """
                            )
                    )),
            @ApiResponse(responseCode = "500", description = "服务器内部错误",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = R.class),
                            examples = @ExampleObject(
                                    value = """
                                            { "code": 500, "msg": "系统异常：NullPointerException", "data": null }
                                            """
                            )
                    ))
    })
    @GetMapping("/getAccessToken")
    public R<String> getAccessToken() {
        try {
            // 1. 生成参数
            String nonce = UUID.randomUUID().toString().replace("-", "");
            long timestamp = System.currentTimeMillis() / 1000;

            // 2. 签名算法
            String signature = generateSignature(APP_ID, APP_SECRET, nonce, timestamp);

            Map<String, Object> params = new HashMap<>();
            params.put("appid", APP_ID);
            params.put("nonce", nonce);
            params.put("timestamp", timestamp);
            params.put("signature", signature);

            // 3. 发送请求
            JSONObject result = httpUtils.post(TOKEN_URL, params);
            log.info("【心永获取Token响应】=> {}", result);

            // 4. 解析返回
            if (result != null && result.getInteger("code") == 0) {
                String token = result.getString("data");
                return R.ok(token, "获取成功");
            } else {
                return R.failed("获取失败: " + (result != null ? result.getString("msg") : "无响应"));
            }

        } catch (Exception e) {
            log.error("获取企业 access_token 异常: ", e);
            return R.failed("系统异常：" + e.getMessage());
        }
    }

    @Operation(
            summary = "签名生成说明",
            description = "内部方法：按默认规则 `md5(appid + nonce + appSecret + timestamp)` 生成签名；若平台规则变更，请调整此方法。",
            hidden = true // 文档隐藏内部实现，保留注解便于维护
    )
    @Parameter(name = "appId", description = "应用的 AppID")
    @Parameter(name = "SecretKey", description = "应用的 AppSecret")
    @Parameter(name = "nonce", description = "随机字符串")
    @Parameter(name = "timestamp", description = "时间戳（秒）")
    /**
     * 生成签名 (符合心永开放平台签名算法)
     * 规则：
     * 1. 将 appid、nonce、timestamp、secretKey 四个参数按 key 升序排序
     * 2. 以 key=value&key=value 形式拼接
     * 3. 使用 MD5 计算摘要，转小写
     */
    private String generateSignature(String appId, String SecretKey, String nonce, long timestamp) throws Exception {
        // 1. 构造参数
        // TreeMap 自动按 key 排序
        Map<String, String> params = new TreeMap<>();
        params.put("appid", appId);
        params.put("nonce", nonce);
        params.put("secretKey", SecretKey);
        params.put("timestamp", String.valueOf(timestamp));

        // 2. 拼接成 "key=value&key=value" 格式
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        // 去掉最后一个 &
        sb.deleteCharAt(sb.length() - 1);

        String signStr = sb.toString();

        // 3. 计算 MD5
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(signStr.getBytes(StandardCharsets.UTF_8));

        StringBuilder md5Hex = new StringBuilder();
        for (byte b : digest) {
            md5Hex.append(String.format("%02x", b & 0xff));
        }

        String signature = md5Hex.toString();
        log.info("【签名原串】{} -> 【MD5结果】{}", signStr, signature);
        return signature;
    }

//    private String generateSignature(String appId, String SecretKey, String nonce, long timestamp) throws Exception {
//        // 注意：你给出的注释与实现顺序略有差异，这里以“平台文档为准”。
//        // 当前实现为 appId + nonce + SecretKey + timestamp
//        String signStr = appId + nonce + SecretKey + timestamp;
//        MessageDigest md = MessageDigest.getInstance("MD5");
//        byte[] digest = md.digest(signStr.getBytes(StandardCharsets.UTF_8));
//
//        StringBuilder sb = new StringBuilder();
//        for (byte b : digest) {
//            sb.append(String.format("%02x", b & 0xff));
//        }
//        return sb.toString();
//    }
}